﻿// ------------------------------------------------------------------------------
//  © 2008 OpenUpon™.  All rights reserved.
// ------------------------------------------------------------------------------
using System;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Configuration;
using System.Web.UI;
using System.Xml;
using Castle.ActiveRecord;
using CastUpon.Entities;
using log4net;
using log4net.Config;

namespace CastUpon {
	public partial class _Default : Page {
		private const string CHANNEL_URI__ROOT_CHANNEL_KEY = "CHANNEL_URI__ROOT_CHANNEL";
		private const string EPISODE_URI__ROOT_CHANNEL_EPISODE_KEY = "EPISODE_URI__ROOT_CHANNEL_EPISODE";
		private const string COMMENT_URI__ROOT_CHANNEL_EPISODE_COMMENT_KEY = "COMMENT_URI__ROOT_CHANNEL_EPISODE_COMMENT";
		private const string COMMENTS_URI__ROOT_CHANNEL_EPISODE_KEY = "COMMENTS_URI__ROOT_CHANNEL_EPISODE";
		private const string RSS_COMMENT_URI__ROOT_CHANNEL_EPISODE_KEY = "RSS_COMMENT_URI__ROOT_CHANNEL_EPISODE";
	
		protected const string COPYRIGHT = "&#xA9;";
		private const string DOMAIN_URI_KEY = "DOMAIN_URI";
		protected const string FEED_NOT_FOUND_MESSAGE = "Feed not found";
		private const string INSTALL_PATH_KEY = "INSTALL_PATH";
		private const string LOGGER_KEY = "LOGGER";
		private const string MEDIA_PATH_KEY = "MEDIA_PATH";

		protected const string NS_ATOM = "http://www.w3.org/2005/Atom";
		protected const string NS_CONTENT = "http://purl.org/rss/1.0/modules/content/";
		protected const string NS_DC = "http://purl.org/dc/elements/1.1/";
		protected const string NS_ITUNES = "http://www.itunes.com/dtds/podcast-1.0.dtd";
		protected const string NS_MEDIA = "http://search.yahoo.com/mrss/";
		protected const string NS_WFW = "http://wellformedweb.org/CommentAPI/";
		

		private static readonly string CHANNEL_URI__ROOT_CHANNEL = WebConfigurationManager.AppSettings[CHANNEL_URI__ROOT_CHANNEL_KEY];
		private static readonly string COMMENT_URI__ROOT_CHANNEL_EPISODE_COMMENT = WebConfigurationManager.AppSettings[COMMENT_URI__ROOT_CHANNEL_EPISODE_COMMENT_KEY];
		private static readonly string COMMENTS_URI__ROOT_CHANNEL_EPISODE = WebConfigurationManager.AppSettings[COMMENTS_URI__ROOT_CHANNEL_EPISODE_KEY];
		private static readonly string EPISODE_URI__ROOT_CHANNEL_EPISODE = WebConfigurationManager.AppSettings[EPISODE_URI__ROOT_CHANNEL_EPISODE_KEY];
		private static readonly string INSTALL_PATH = WebConfigurationManager.AppSettings[INSTALL_PATH_KEY];
		private static readonly ILog log = LogManager.GetLogger(WebConfigurationManager.AppSettings[LOGGER_KEY]);
		private static readonly string MEDIA_PATH = WebConfigurationManager.AppSettings[MEDIA_PATH_KEY];

		private static readonly string ROOT_URI = WebConfigurationManager.AppSettings[DOMAIN_URI_KEY];
		private static readonly string RSS_COMMENT_URI__ROOT_CHANNEL_EPISODE = WebConfigurationManager.AppSettings[RSS_COMMENT_URI__ROOT_CHANNEL_EPISODE_KEY];

		private string finalRootUri = ROOT_URI;

		protected static void generateXmlITunesCategories(XmlTextWriter xml, PodcastChannel channel) {
			foreach(iTunesCategory category in channel.iTunesCategories) {
				generateXmlForOneITunesCategory(xml, category);
			}
		}

		protected static void generateXmlForOneITunesCategory(XmlTextWriter xml, iTunesCategory category) {
			xml.WriteStartElement("itunes:category"); // <itunes:category>
			xml.WriteAttributeString("text", category.Category);

			foreach(iTunesCategory subCategory in category.Children) {
				generateXmlForOneITunesCategory(xml, subCategory);
			}
			xml.WriteEndElement(); // </itunes:category>
		}

		protected void generateXmlForItems(XmlTextWriter xml, PodcastChannel channel) {
			foreach(Podcast castItem in channel.Podcasts) {
				xml.WriteStartElement("item"); // <item>
				xml.WriteElementString("title", castItem.Title);
				xml.WriteElementString("link", String.Format(EPISODE_URI__ROOT_CHANNEL_EPISODE, this.finalRootUri + INSTALL_PATH, channel.Id, castItem.Id));
				xml.WriteElementString("comments", String.Format(COMMENTS_URI__ROOT_CHANNEL_EPISODE, this.finalRootUri + INSTALL_PATH, channel.Id, castItem.Id));
				xml.WriteElementString("pubDate", castItem.PublishDate.ToString("r"));
				xml.WriteElementString("dc:creator", castItem.Author);

				xml.WriteStartElement("category"); // <category>
				xml.WriteCData("podcasts");
				xml.WriteEndElement(); // </category>

				xml.WriteStartElement("guid"); // <guid>
				xml.WriteAttributeString("isPermaLink", "false");
				xml.WriteString(String.Format(EPISODE_URI__ROOT_CHANNEL_EPISODE, this.finalRootUri + INSTALL_PATH, channel.Id, castItem.Id));
				xml.WriteEndElement(); // </guid>

				xml.WriteStartElement("description"); // <description>
				xml.WriteCData(castItem.Content ?? String.Empty);
				xml.WriteEndElement(); // </description>

				xml.WriteStartElement("content:encoded"); // <content:encoded>
				xml.WriteCData(castItem.ContentEncodedNoNewLines ?? String.Empty);
				xml.WriteEndElement(); // </content:encoded>

				xml.WriteElementString("wfw:commentRss", String.Format(RSS_COMMENT_URI__ROOT_CHANNEL_EPISODE, this.finalRootUri + INSTALL_PATH, channel.Id, castItem.Id));

				xml.WriteStartElement("enclosure"); // <enclosure>
				xml.WriteAttributeString("url", String.Format("{0}/{1}", this.finalRootUri + MEDIA_PATH, castItem.FilePath));
				xml.WriteAttributeString("length", castItem.FileSize);
				xml.WriteAttributeString("type", castItem.FileType);
				xml.WriteEndElement(); // </enclosure>

				xml.WriteElementString("itunes:duration", castItem.Duration.Hours <= 0 ? new DateTime(castItem.Duration.Ticks).ToString("00:mm:ss") : new DateTime(castItem.Duration.Ticks).ToString("hh:mm:ss"));
				xml.WriteElementString("itunes:subtitle", castItem.SubTitle);
				xml.WriteElementString("itunes:summary", castItem.Description);
				xml.WriteElementString("itunes:keywords", castItem.Keywords);
				xml.WriteElementString("itunes:author", castItem.Author);
				xml.WriteElementString("itunes:explicit", "no");
				xml.WriteElementString("itunes:block", "no");

				xml.WriteEndElement(); // </item>
			}
		}

		protected void generateXmlForCommentItems(XmlTextWriter xml, Podcast podcast) {
			foreach(Comment comment in podcast.Comments) {
				xml.WriteStartElement("item"); // <item>
				xml.WriteElementString("title", String.Format("By: {0}", comment.Author));
				xml.WriteElementString("link", String.Format(COMMENT_URI__ROOT_CHANNEL_EPISODE_COMMENT, this.finalRootUri + INSTALL_PATH, podcast.Channel.Id, podcast.Id, comment.Id));
				xml.WriteElementString("comments", String.Format(COMMENTS_URI__ROOT_CHANNEL_EPISODE, this.finalRootUri + INSTALL_PATH, podcast.Channel.Id, podcast.Id));
				xml.WriteElementString("pubDate", comment.DateCreated.ToString("r"));
				xml.WriteElementString("dc:creator", podcast.Author);

				xml.WriteStartElement("guid"); // <guid>
				xml.WriteAttributeString("isPermaLink", "false");
				xml.WriteString(String.Format(COMMENT_URI__ROOT_CHANNEL_EPISODE_COMMENT, this.finalRootUri + INSTALL_PATH, podcast.Channel.Id, podcast.Id, comment.Id));
				xml.WriteEndElement(); // </guid>

				xml.WriteStartElement("description"); // <description>
				xml.WriteString(comment.ContentEncoded != null ? Regex.Replace(comment.ContentEncoded, "<[^>]*>", string.Empty) : String.Empty);
				xml.WriteEndElement(); // </description>

				xml.WriteStartElement("content:encoded"); // <content:encoded>
				xml.WriteCData(comment.ContentEncoded ?? String.Empty);
				xml.WriteEndElement(); // </content:encoded>

				xml.WriteEndElement(); // </item>
			}
		}

		protected void Page_PreRender(object sender, EventArgs e) {
			XmlConfigurator.Configure();

			if(this.finalRootUri.CompareTo("auto") == 0) {
				bool secureUri = Request.ServerVariables["SERVER_PORT_SECURE"].CompareTo("1") == 0;
				int uriPort = Convert.ToInt32(Request.ServerVariables["SERVER_PORT"]);
				bool portExists = false;
				if((secureUri && uriPort != 443) ||
				   (!secureUri && uriPort != 80)) {
					portExists = true;
				}
				this.finalRootUri = String.Format("{0}://{1}{2}",
				                                  secureUri ? "https" : "http",
				                                  Request.ServerVariables["SERVER_NAME"],
				                                  portExists ? ":" + uriPort : String.Empty);
			}

			ListenerLog listenerLogEntry = new ListenerLog();
			listenerLogEntry.Agent = Request.UserAgent;
			listenerLogEntry.Referrer = Request.UrlReferrer != null ? Request.UrlReferrer.ToString() : String.Empty;
			listenerLogEntry.Uri = Request.RawUrl;
			listenerLogEntry.IpAddress = Request.UserHostAddress;

			ListenerLog.AddLogEntry(listenerLogEntry);

			if(Request.QueryString["type"] != null && Request.QueryString["channel"] != null &&
			   Request.QueryString["feed"] != null) {
				Podcast podcast = Podcast.GetPodcastByChannelAndId(Int32.Parse(Request.QueryString["channel"]), Int32.Parse(Request.QueryString["feed"]));
				if(podcast != null) {
					switch(Request.QueryString["type"]) {
						case "episode": {
							Form.Visible = true;
							this.NoChannelsPanel.Visible = false;
							this.Episode.Visible = true;
							// SessionScope is used so that ActiveRecord/NHibernate can keep track of the session for lazy loading persistance.
							using(new SessionScope()) {
								this.Listeners.Text = ListenerLog.NumberOfListeners().ToString();
								this.TitleLiteral.Text = podcast.Title ?? String.Empty;
								this.TitleLink.Title = String.Format("Permanent Link to {0}", podcast.Title);
								this.TitleLink.HRef = String.Format(EPISODE_URI__ROOT_CHANNEL_EPISODE, this.finalRootUri + INSTALL_PATH, Request.QueryString["channel"], Request.QueryString["feed"]);
								this.AuthorLiteral.Text = podcast.Author;
								this.PostedDateLiteral.Text = podcast.PublishDate.ToString("ddd, MMMM dd \\a\\t hh:mm tt");
								this.ArticleLiteral.Text = podcast.ContentEncodedNoNewLines;
								this.CommentRepeater.DataSource = podcast.Comments;
								this.CommentRepeater.DataBind();
								this.ResponseCountLiteral.Text = String.Format("{0} Response{1}", podcast.Comments.Count, podcast.Comments.Count != 1 ? "s" : String.Empty);
							}
							break;
						}

						case "comments": {
							Form.Visible = true;
							this.NoChannelsPanel.Visible = false;
							this.Episode.Visible = true;
							// SessionScope is used so that ActiveRecord/NHibernate can keep track of the session for lazy loading persistance.
							using(new SessionScope()) {
								Response.Clear();
								Response.HeaderEncoding = Encoding.UTF8;
								Response.ContentEncoding = Encoding.UTF8;
								Response.ContentType = "application/rss+xml";

								using(XmlTextWriter xml = new XmlTextWriter(Response.Output)) {
									xml.Formatting = Formatting.Indented;
									xml.Indentation = 1;
									xml.IndentChar = Convert.ToChar("\t");
									xml.WriteStartDocument();
									xml.WriteStartElement("rss"); // <rss>
									xml.WriteAttributeString("version", "2.0");
									xml.WriteAttributeString("xmlns:content", NS_CONTENT);
									xml.WriteAttributeString("xmlns:dc", NS_DC);
									xml.WriteAttributeString("xmlns:atom", NS_ATOM);

									xml.WriteStartElement("channel"); // <channel>
									xml.WriteElementString("title", String.Format("Comments on: {0})", podcast.Title));

									xml.WriteStartElement("atom:link"); // <atom:link>
									xml.WriteAttributeString("href", String.Format(RSS_COMMENT_URI__ROOT_CHANNEL_EPISODE, this.finalRootUri + INSTALL_PATH, Request.QueryString["channel"], Request.QueryString["feed"]));
									xml.WriteAttributeString("rel", "self");
									xml.WriteAttributeString("type", "application/rss+xml");
									xml.WriteEndElement(); // </atom:link>

									xml.WriteElementString("link", String.Format(EPISODE_URI__ROOT_CHANNEL_EPISODE, this.finalRootUri + INSTALL_PATH, Request.QueryString["channel"], Request.QueryString["feed"]));

									xml.WriteElementString("description", podcast.Description);
									xml.WriteElementString("pubDate", podcast.PublishDate.ToString("r"));
									xml.WriteElementString("generator", String.Format("CastUpon {0}", Assembly.GetAssembly(GetType()).GetName().Version));

									generateXmlForCommentItems(xml, podcast);

									xml.WriteEndElement(); // </channel>
									xml.WriteEndElement(); // </rss>
									xml.WriteEndDocument();
								}

								Response.End();
							}
							break;
						}
					}
				}
				else {
					this.TitleLiteral.Text = FEED_NOT_FOUND_MESSAGE;
				}
			}
			else if(PodcastChannel.GetChannelsCount() == 1) {
				Form.Visible = false;
				this.NoChannelsPanel.Visible = true;
				this.Episode.Visible = false;
				PodcastChannel[] channels = ActiveRecordBase<PodcastChannel>.FindAll();
				PodcastChannel mainChannel = channels[0];

				try {
					Response.Clear();
					Response.HeaderEncoding = Encoding.UTF8;
					Response.ContentEncoding = Encoding.UTF8;
					Response.ContentType = "application/rss+xml";

					using(XmlTextWriter xml = new XmlTextWriter(Response.Output)) {
						xml.Formatting = Formatting.Indented;
						xml.Indentation = 1;
						xml.IndentChar = Convert.ToChar("\t");
						xml.WriteStartDocument();
						xml.WriteStartElement("rss"); // <rss>
						xml.WriteAttributeString("version", "2.0");
						xml.WriteAttributeString("xmlns:content", NS_CONTENT);
						xml.WriteAttributeString("xmlns:wfw", NS_WFW);
						xml.WriteAttributeString("xmlns:dc", NS_DC);
						xml.WriteAttributeString("xmlns:atom", NS_ATOM);
						xml.WriteAttributeString("xmlns:itunes", NS_ITUNES);
						xml.WriteAttributeString("xmlns:media", NS_MEDIA);

						xml.WriteStartElement("channel"); // <channel>
						xml.WriteElementString("title", mainChannel.Title);

						xml.WriteStartElement("atom:link"); // <atom:link>
						xml.WriteAttributeString("href", String.Format(CHANNEL_URI__ROOT_CHANNEL, this.finalRootUri + INSTALL_PATH, mainChannel.Id));
						xml.WriteAttributeString("rel", "self");
						xml.WriteAttributeString("type", "application/rss+xml");
						xml.WriteEndElement(); // </atom:link>

						xml.WriteElementString("link", String.Format(CHANNEL_URI__ROOT_CHANNEL, this.finalRootUri + INSTALL_PATH, mainChannel.Id));

						xml.WriteElementString("description", mainChannel.Description);
						xml.WriteElementString("pubDate", mainChannel.PublishDate.ToString("r"));
						xml.WriteElementString("generator", String.Format("CastUpon {0}", Assembly.GetAssembly(GetType()).GetName().Version));
						xml.WriteElementString("language", mainChannel.Language);
						xml.WriteElementString("copyright", String.Format("{0} {1}", mainChannel.Copyright, mainChannel.Author));
						xml.WriteElementString("managingEditor", String.Format("{0} ({1})", mainChannel.AuthorEmail, mainChannel.Author));
						xml.WriteElementString("webMaster", String.Format("{0} ({1})", mainChannel.AuthorEmail, mainChannel.Author));
						xml.WriteElementString("category", mainChannel.Category);
						xml.WriteElementString("ttl", mainChannel.Ttl.ToString());
						xml.WriteElementString("itunes:subtitle", mainChannel.SubTitle);
						xml.WriteElementString("itunes:summary", mainChannel.SubTitle);
						xml.WriteElementString("itunes:author", mainChannel.Author);
						xml.WriteElementString("itunes:keywords", mainChannel.Keywords);
						generateXmlITunesCategories(xml, mainChannel);

						xml.WriteStartElement("itunes:owner"); // <itunes:owner>
						xml.WriteElementString("itunes:name", mainChannel.Author);
						xml.WriteElementString("itunes:email", mainChannel.AuthorEmail);
						xml.WriteEndElement(); // </itunes:owner>

						xml.WriteElementString("itunes:block", "no");
						xml.WriteElementString("itunes:explicit", "no");

						xml.WriteStartElement("itunes:image"); // <itunes:image>
						xml.WriteAttributeString("href", this.finalRootUri + mainChannel.iTunesImagePath);
						xml.WriteEndElement(); // </itunes:image>

						xml.WriteStartElement("image"); // <image>
						xml.WriteElementString("url", this.finalRootUri + mainChannel.ImagePath);
						xml.WriteElementString("title", mainChannel.Title);
						xml.WriteElementString("link", String.Format(CHANNEL_URI__ROOT_CHANNEL, this.finalRootUri + INSTALL_PATH, mainChannel.Id));
						xml.WriteElementString("width", mainChannel.ImageWidth.ToString());
						xml.WriteElementString("height", mainChannel.ImageHeight.ToString());
						xml.WriteEndElement(); // </image>

						generateXmlForItems(xml, mainChannel);

						xml.WriteEndElement(); // </channel>
						xml.WriteEndElement(); // </rss>
						xml.WriteEndDocument();
					}

					Response.End();
				}
				catch(Exception ex) {
					log.Info(ex.ToString());
					throw;
				}
			}
		}

		protected void Submit_Click(object sender, EventArgs e) {
			string contentPosted = Request.Form[this.PostCommentControl.TextboxControl.ClientID];
			if(Page.IsValid) {
				Comment currentComment = new Comment();
				currentComment.Podcast = Podcast.GetPodcastByChannelAndId(Int32.Parse(Request.QueryString["channel"]), Int32.Parse(Request.QueryString["feed"]));
				currentComment.IpAddress = Request.UserHostAddress;
				currentComment.DateCreated = DateTime.Now;
				currentComment.Author = this.PostCommentControl.Author;
				currentComment.Email = this.PostCommentControl.Email;
				currentComment.Website = this.PostCommentControl.Website;
				currentComment.ContentEncoded = HtmlSanitizer.SanitizeHtml(contentPosted);
				currentComment.Save();
				this.PostCommentControl.Content = String.Empty;
			}
			else {
				this.PostCommentControl.Content = contentPosted;
			}
		}
	}
}